package com.thoughtworks.impersonator.http.message.storage;

import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.matchers.JUnitMatchers.containsString;
import static org.junit.matchers.JUnitMatchers.hasItem;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.thoughtworks.impersonator.http.message.HttpExchange;
import com.thoughtworks.impersonator.http.message.RawRequest;
import com.thoughtworks.impersonator.http.message.RawResponse;
import com.thoughtworks.impersonator.http.message.transform.DefaultRequestTransformer;
import com.thoughtworks.impersonator.http.message.transform.DefaultResponseTransformer;
import com.thoughtworks.impersonator.http.test.FixedUidRequestIdentifier;

public class HttpExchangeStorageIntegrationTest {

    private File storageDir;
    private HttpExchangeStorage storage;

    @Before
    public void setupFixtures() throws IOException {
        storageDir = new File("target/integration-tests/storage");
        FileUtils.forceMkdir(storageDir);
        storage = new HttpExchangeStorage(storageDir, new HttpExchangeSerialiser(new FixedUidRequestIdentifier("list-all"),
                new DefaultRequestTransformer(), new DefaultResponseTransformer()));
    }
    
    @After
    public void cleanup() throws IOException {
        FileUtils.forceDelete(storageDir);
    }

    @Test
    public void shouldListAllExchangesStored() throws Exception {
        populate(storageDir, requestFile("a", "a body"), responseFile("a", "a body"), requestFile("b", "b body"),
                responseFile("b", "b body"));

        List<HttpExchange> exchanges = storage.all();

        assertThat(exchanges.size(), is(2));
    }
    
    @Test
    @SuppressWarnings("unchecked")
    public void shouldSaveNewExchangesToStorage() throws Exception {
        HttpExchange exchange = new HttpExchange(requestWithContents("whatever request"), responseWithContents("whatever response"));
        
        storage.store(exchange);
        
        assertThat(theContentsOf(storageDir.listFiles()), allOf(
                hasItem(containsString("whatever request")),
                hasItem(containsString("whatever response"))));
    }
    
    @Test
    public void shouldFindExchangesBasedOnRequests() throws Exception {
        HttpRequest request = requestWithContents("whatever request");
        HttpResponse response = responseWithContents("whatever response");
        HttpExchange exchange = new HttpExchange(request, response);
        storage.store(exchange);

        HttpResponse loadedResponse = storage.loadResponseFor(request);

        assertThat(contentsFor(loadedResponse), equalTo(contentsFor(response)));
    }

    @Test
    public void shouldSaveUnknownRequests() throws Exception {
        HttpRequest request = requestWithContents("whatever request");

        storage.loadResponseFor(request);

        assertThat(unknownFileDirContents().size(), is(1));
    }
    
    private List<File> unknownFileDirContents() {
        return Arrays.asList(storage.getUnkwownFilesDirectory().listFiles());
    }

    private List<String> theContentsOf(File[] listFiles) {
        List<String> fileContents = new ArrayList<String>();
        for (File file : listFiles) {
            String fileContent;
            try {
                fileContent = FileUtils.readFileToString(file);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            fileContents.add(fileContent);
        }
        return fileContents;
    }
    
    private HttpResponse responseWithContents(String contents) {
        FileDescriptor responseFile = responseFile("response", contents);
        return new RawResponse(responseFile.contents).toHttpResponse();
    }

    private HttpRequest requestWithContents(String contents) {
        FileDescriptor requestFile = requestFile("request", contents);
        return new RawRequest(requestFile.contents).toHttpRequest();
    }

    private void populate(File directory, FileDescriptor... files) throws IOException {
        for (FileDescriptor file : files) {
            FileUtils.writeStringToFile(new File(directory, file.name), file.contents);
        }
    }

    private FileDescriptor requestFile(String name, String body) {
        String requestTemplate = "GET / HTTP/1.1\r\nHost: localhost\r\nContent-Length: %d\r\n\r\n%s\r\n\r\n";
        return new FileDescriptor(name + ".request", String.format(requestTemplate, body.length(), body));
    }

    private FileDescriptor responseFile(String name, String body) {
        String requestTemplate = "HTTP/1.1 200 OK\r\nDate: Tue, 16 Feb 2010 11:55:47 GMT\r\n"
                + "Content-Length: %d\r\n\r\n%s\r\n\r\n";
        return new FileDescriptor(name + ".response", String.format(requestTemplate, body.length(), body));
    }

    private class FileDescriptor {

        private final String name;
        private final String contents;

        public FileDescriptor(String name, String contents) {
            this.name = name;
            this.contents = contents;
        }

    }
    
    private String contentsFor(HttpResponse response) {
        return response.getContent().toString("utf-8");
    }


}
